#include <texture_bin.h>
#include <model.h>

#include <cmath>

texture_bin::texture_bin() : bin() {
}

texture_bin::~texture_bin() {
}

void
texture_bin::init(model *parent, pngstream *_pngs, int _level, int _offset_x, int _offset_y) {
  this->p = parent;
  this->pngs = _pngs;

  this->row_count = 0;

  this->level = _level;
  this->lcoef = (int) pow(2.0, (double) level);

  this->offset_x = _offset_x / lcoef;
  this->offset_y = _offset_y / lcoef;

  this->chbpp = (pngs->pngheader.bitdepth * pngs->pngheader.channels) / 8;
  this->colsize = p->t_tile_size[0] * lcoef;
  this->rowsize = p->t_tile_size[1] * lcoef;
  this->numtiles_x = pngs->pngheader.width / rowsize;

  this->rows = new unsigned char *[rowsize];
  this->tile = new unsigned char *[rowsize];
}

void 
texture_bin::addRow(unsigned char *row) {
  rows[row_count] = row;
  row_count++;

  if (row_count == p->t_tile_size[1] * lcoef) {
    generateTiles();
  }
}

void
texture_bin::generateTiles() {
  
  // tile the input image rows data
  for (int i = 0; i != numtiles_x; i++) {
    jpgstream *jpgs = new jpgstream();
    FILE *fout = fopen("temp.jpg", "wb");
    jpgs->initializeOutput(fout, rowsize, colsize, pngs->pngheader.channels, 1, 1);
        
    for (int j = 0; j != colsize; j++) {
      tile[j] = &rows[j][i * rowsize * chbpp];
    }

    jpgs->write(tile);
    jpgs->finishOutput();
    delete jpgs;
    fclose(fout);
        
    // resize level 0 tile to requested level
    for (int k = 0; k < level; k++) {
      FILE *fin_scl = fopen("temp.jpg", "rb");

      jpgstream *jpgs_scale = new jpgstream();
      jpgs_scale->initializeInput(fin_scl);

      jpgstream::jpg_info header;
      header = jpgs_scale->jpgheader;

      unsigned char **scale_rows = new unsigned char *[header.height];

      int index = 0;
      while (jpgs_scale->hasNext()) {
        scale_rows[index] = jpgs_scale->readLine();
        index++;
      }

      jpgs_scale->finishInput();
      delete jpgs_scale;
      fclose(fin_scl);

      FILE *fout_scl = fopen("temp.jpg", "wb");

      jpgs_scale = new jpgstream();
      jpgs_scale->initializeOutput(fout_scl, 
                                   header.width,
                                   header.height,
                                   header.nc,
                                   1,
                                   2);           

      jpgs_scale->write(scale_rows);
      jpgs_scale->finishOutput();
      for (int m = 0; m != index; m++) {
        delete scale_rows[m];
      }

      delete scale_rows;
      fclose(fout_scl);
    }

    // stream image to database
    fstream input_stream("temp.jpg",  fstream::in | fstream::binary);

    stringstream query;

    int id = (p->t_size[0] / colsize) * offset_y + offset_x + i;
    vec2i ratio((p->t_size[0] / p->hm_size[0]) / (p->t_tile_size[0] / p->hm_tile_size[0]),
                (p->t_size[1] / p->hm_size[1]) / (p->t_tile_size[1] / p->hm_tile_size[1]));
    int hm_tile = ((p->t_size[0] / colsize) * offset_y / ratio[1]) + (offset_x + i) / ratio[0];

    query << "INSERT INTO tex_tiles_" << level
          << " (id, fk_hm_tile, data) VALUES ("
          << id << "," << hm_tile << "," << "?)";

    try {
      std::auto_ptr<sql::PreparedStatement> prepstmt(p->getDB().prepareQuery(query.str()));
      prepstmt->setBlob(1, &input_stream);
      prepstmt->execute();
      prepstmt->close();
    }
    catch (SQLException &e) {
      cerr << "ERROR: " << e.what();
      cerr << " (MySQL error code: " << e.getErrorCode();
      cerr << ", SQLState: " << e.getSQLState() << ")" << endl;
    }

    input_stream.close();
  }

  for (int j = 0; j != row_count; j++) {
    delete [] rows[j];
    rows[j] = NULL;
  }

  row_count = 0;
  offset_y += 1;
}